/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.bef.core.lock;

import com.inspur.edp.bef.core.DebugAdapter;
import com.inspur.edp.cef.api.RefObject;
import com.inspur.edp.cef.entity.dependenceTemp.DataValidator;
import io.iec.edp.caf.boot.context.CAFContext;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import io.iec.edp.caf.core.session.SessionType;
import io.iec.edp.caf.lockservice.api.BatchLockResult;
import io.iec.edp.caf.lockservice.api.DataLockOptions;
import io.iec.edp.caf.lockservice.api.LockedScope;
import io.iec.edp.caf.lockservice.api.ReplacedScope;
import io.iec.edp.caf.lock.service.api.api.LockService;
import io.iec.edp.caf.sysmanager.api.data.user.User;
import io.iec.edp.caf.sysmanager.api.manager.user.UserManager;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import org.springframework.util.StringUtils;

public class LockAdapter
{
	private final String lockMkID = "DEV";
	private final String lockFuncID = "";
	private final String lockComment = "lockByBefRuntime";
	private final String isLockedComment = "lockByBefRuntime-islocked";

	//使用独占锁
	private static final DataLockOptions lockOptions = new DataLockOptions(Duration.ZERO, ReplacedScope.Exclusive, LockedScope.BizContext,Duration.ZERO);
	private static final DataLockOptions tccLockOptions = new DataLockOptions(Duration.ZERO, ReplacedScope.Exclusive, LockedScope.AppInstance, Duration.ZERO);
//	private static final DataLockOptions lockOptions = new DataLockOptions(Duration.ZERO, ReplacedScope.Self, LockedScope.Session,Duration.ZERO);

	private static final LockAdapter instance = new LockAdapter();

	public static LockAdapter getInstance(){return instance;}

	private LockService innerLockSvr;

	public LockAdapter() {
		//单元测试需要注释
		innerLockSvr = SpringBeanUtils.getBean(LockService.class);
	}

	public final boolean addLock(String beType, List<String> dataIds, RefObject<String> groupId, RefObject<java.util.List<LockEntity>> lockEntities) {
		trace("addingLock", dataIds, null);
		BatchLockResult lockResult = innerLockSvr.addBatchLock(lockMkID, beType,dataIds,groupId.argvalue,lockOptions, lockFuncID,
				lockComment);
		groupId.argvalue = lockResult.getGroupId();
		boolean success = lockResult.isSuccess();
		LockEntity failedLockEntity = new LockEntity();
		trace("addLock" + (success ? "Success" : "Failed"), groupId.argvalue, null);
		if(!success){
			Objects.requireNonNull(lockResult.getLockedEntities(), "lockResult.getLockedEntities()");
			if(!StringUtils.isEmpty(lockResult.getLockedEntities().get(0).getUserId())){
				User user = SpringBeanUtils.getBean(UserManager.class).getUser(
					lockResult.getLockedEntities().get(0).getUserId());
				if(user != null){
					failedLockEntity.setUserName(user.getName());
				}
			}
			failedLockEntity.setDataId(dataIds.get(0));
			lockEntities.argvalue = Arrays.asList(failedLockEntity);
		}
		trace("addLock" + (success ? "Success" : "Failed"), groupId.argvalue, null);
		return success;
	}

	public final boolean addIgnoreLock(String beType, List<String> dataIds, String groupId) {

		trace("addingIgnoreLock", dataIds, null);
		boolean success = isIgnoreLock(beType,dataIds,groupId);
		trace("addLockToGroup" + (success ? "Success" : "Failed"), groupId, null);
		return success;
	}

	private boolean isIgnoreLock(String beType, List<String> dataIds,String groupId){
		List<String> data = new ArrayList<>();
		for(int i = 0 ; i<=dataIds.size()-1 ; i++) {
			data.clear();
			data.add(dataIds.get(i));
			BatchLockResult lockResult = innerLockSvr.addBatchLock(lockMkID, beType, data, groupId, lockOptions, lockFuncID, lockComment);
			Objects.requireNonNull(lockResult.getLockedEntities(), "lockResult.getLockedEntities()");
			if(!CAFContext.current.getSessionId().equals(lockResult.getLockedEntities().get(0).getSessionId()) || !CAFContext.current.getUserId().equals(lockResult.getLockedEntities().get(0).getUserId())){
				return false;
			}
		}
		return true;
	}

	public final boolean addLock2Group(String beType, List<String> dataIds, String groupId, RefObject<java.util.List<LockEntity>> lockEntities) {
//		lockEntities.argvalue = dataIds.stream().map(id -> {
//			LockEntity rez = new LockEntity();
//			rez.setDataId(id);
//			rez.setUserName("");
//			return rez;
//		}).collect(Collectors.toList());
//		return true;
		trace("addingLockToGroup", dataIds, null);
		DataValidator.checkForEmptyString(groupId,"groupId");
		BatchLockResult lockResult = innerLockSvr.addBatchLock(lockMkID, beType, dataIds,groupId, lockOptions, lockFuncID,
				lockComment);
		boolean success = lockResult.isSuccess();
		trace("addLockToGroup" + (success ? "Success" : "Failed"), groupId, null);
		return success;
	}

	private final static void trace(String mark, Object info, Exception e){
		DebugAdapter.trace("DataLock", mark, e, info);
	}

	public final void releaseLock(String groupId) {
		trace("ReleaseDataLockByGroup", groupId, null);
		//return;
		innerLockSvr.removeBatchLock(groupId);
	}

	public boolean addTccLock(String beType, List<String> dataIds, RefObject<String> groupId) {
		Objects.requireNonNull(beType);
		Objects.requireNonNull(dataIds);
		Objects.requireNonNull(groupId);

		BatchLockResult lockResult = innerLockSvr.addBatchLock(lockMkID, beType, dataIds, null,tccLockOptions, lockFuncID,lockComment);
		if(lockResult.isSuccess()) {
			groupId.argvalue = lockResult.getGroupId();
		}
		return lockResult.isSuccess();
	}

	//判断数据是否已被加锁, 用于临时解决广水移动端自己锁自己
	public boolean isLocked(String beType, List<String> dataIds) {
		Objects.requireNonNull(beType);
		Objects.requireNonNull(dataIds);
		BatchLockResult lockResult = innerLockSvr.addBatchLock(lockMkID, beType, dataIds, null, Duration.ofMillis(5), lockFuncID, isLockedComment);
		boolean rez =lockResult.isSuccess();
		if(rez) {
			innerLockSvr.removeBatchLock(lockResult.getGroupId());
		}
		return !rez;
	}

	public Collection<String> getDataIdsByGroupId(String id) {
		return innerLockSvr.getDataIds(id); //Collections.emptyList();
	}

	public static class LockEntity {
		private String privateDataId;
		public final String getDataId()
		{
			return privateDataId;
		}
		public final void setDataId(String value)
		{
			privateDataId = value;
		}
		private String privateUserName;
		public final String getUserName()
		{
			return privateUserName;
		}
		public final void setUserName(String value)
		{
			privateUserName = value;
		}
	}


	private static SessionType targetType = SessionType.mobile;
	public static boolean isMobileClient() {
		return CAFContext.current.getCurrentSession().getSessionType() == targetType;
	}

	public void removeBatchLockByContext(String bizCtxId){
		innerLockSvr.removeBatchLockByContext(bizCtxId);
	}
}
